package com.wenyl.interview.redis.service.impl;

import com.alibaba.fastjson.JSON;
import com.wenyl.interview.redis.dto.GoodsCacheData;
import com.wenyl.interview.redis.service.IGoodsService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author Swimming Dragon
 * @description: TODO
 * @date 2024年09月25日 23:09
 */
@Slf4j
@Service
public class GoodsServiceImpl implements IGoodsService {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    private static final String GOODS_LOCK_KEY = "GOODS_LOCK_KEY:";
    @Override
    public String getById(String goodsId) {
        String cachedGoodsInfo = redisTemplate.opsForValue().get(goodsId);
        //这里数据是存在的只是缓存中没有，所以直接模拟返回数据
        if(!StringUtils.hasLength(cachedGoodsInfo)){
            try{
                System.out.println("缓存没有数据，查询数据库");
                // 模拟从数据库加载
                Thread.sleep(100);
                // 这里查询到数据后是要更新到缓存的，但是高并发下，可能存在多个线程同时更新的问题
                redisTemplate.opsForValue().set(goodsId,"商品信息");
                return "商品信息";
            }catch (InterruptedException e){
                throw new RuntimeException("线程休眠异常");
            }
        }
        return cachedGoodsInfo;
    }

    @Override
    public String getByIdv2(String goodsId) {
        String cachedGoodsInfo = redisTemplate.opsForValue().get(goodsId);
        //数据不存在
        if(!StringUtils.hasLength(cachedGoodsInfo)){
            // 缓存没有数据
            RLock rLock = redissonClient.getLock(GOODS_LOCK_KEY+goodsId);
            try{
                rLock.lock();
                // 再次判断若缓存无数据则才更新缓存，确定只有一个线程会执行
                if(!StringUtils.hasLength(redisTemplate.opsForValue().get(goodsId))){
                    System.out.println("缓存没有数据，获取锁更新数据到缓存");
                    // 模拟从数据库加载
                    Thread.sleep(500);
                    String goodsInfo = "商品信息";
                    redisTemplate.opsForValue().set(goodsId,goodsInfo);
                    System.out.println("成功更新数据到缓存");
                    return goodsInfo;
                }
            }catch (InterruptedException e){
                throw new RuntimeException("线程休眠异常");
            }finally {
                System.out.println("释放锁");
                rLock.unlock();
            }
        }
        return cachedGoodsInfo;
    }

    @Override
    public String getByIdv3(String goodsId) {
        // 查询缓存
        String goodsInfoStr = redisTemplate.opsForValue().get(goodsId);

        // 缓存不存在
        if(!StringUtils.hasLength(goodsInfoStr)){
            /*
            * 这里需要根据数据是否真实存在，采取缓存穿透或缓存击穿的处理方法
            * 这里我默认数据存在，缓存一个数据，然后返回
            * */
            try {
                Thread.sleep(100);
                String goodsName = "手机";
                cacheData(goodsId,goodsName,2);
                return goodsName;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        // 缓存存在，判断逻辑时间是否过期
        GoodsCacheData cachedObj = JSON.toJavaObject(JSON.parseObject(goodsInfoStr), GoodsCacheData.class);
        // 当前时间超过逻辑时间
        if(LocalDateTime.now().isAfter(cachedObj.getExpireTime())){
            RLock lock = redissonClient.getLock(GOODS_LOCK_KEY + goodsId);
            try{
                lock.lock();
                cachedObj = JSON.toJavaObject(JSON.parseObject(goodsInfoStr), GoodsCacheData.class);
                if(LocalDateTime.now().isAfter(cachedObj.getExpireTime())){
                    System.out.println("缓存逻辑过期");
                    ExecutorService executorService = Executors.newFixedThreadPool(1);
                    executorService.execute(() -> {
                        // 重新查询数据，更新缓存
                        cacheData(goodsId,"手机更新后",2);
                    });
                }
                return cachedObj.getGoodsName();
            }catch (Exception e){
                log.error("异常: ", e);
            }finally {
                System.out.println("释放锁");
                lock.unlock();
            }
        }
        // 这里默认直接返回老的数据值
        return cachedObj.getGoodsName();
    }

    /**
     * 商品添加逻辑过期时间并缓存
     * @param goodsId 商品id
     * @param goodsName 商品名称
     * @param expireTime 逻辑过期时间（秒）
     */
    private void cacheData(String goodsId,String goodsName,long expireTime){
        GoodsCacheData goodsCacheData = new GoodsCacheData();
        goodsCacheData.setGoodsId(goodsId);
        goodsCacheData.setGoodsName(goodsName);
        goodsCacheData.setExpireTime(LocalDateTime.now().plusSeconds(expireTime));
        redisTemplate.opsForValue().set(goodsId, JSON.toJSONString(goodsCacheData));
    }
}
